/***************************************************************
*  Copyright (c) 2007 by GroupMe! Team (www.groupme.net)
*  All rights reserved.
*
*  This file is part of the GroupMe! Project. Source code of 
*  this project is closed and redistribution of this code is
*  prohibited. 
*  
*  Contact: http://www.groupme.net
*
*  This copyright notice MUST APPEAR in all copies of the file!
***************************************************************/
package net.groupme.controller.ajax;

import java.net.URI;
import java.net.URISyntaxException;
import java.text.DateFormat;
import java.util.Calendar;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import net.groupme.controller.model.HtmlFormattedElement;
import net.groupme.exceptions.search.SearchEngineException;
import net.groupme.gui.constants.VIEW_VOCABULARY;
import net.groupme.gui.rendering.TagRenderer;
import net.groupme.model.Group;
import net.groupme.model.ObjectFactory;
import net.groupme.model.Resource;
import net.groupme.model.User;
import net.groupme.search.SEARCH_CONFIGURATION;
import net.groupme.search.SearchManager;
import net.groupme.search.strategy.model.WeightedTag;
import net.groupme.user.UserManager;

/**
 * created on Jul 4, 2007
 * 
 * This Ajax controller manages requests which concern the internal GroupMe! search. 
 * 
 * @author Fabian Abel, <a href="mailto:abel@l3s.de">abel@l3s.de</a>
 * @author last edited by: $Author: nicole $
 * 
 * @version $Revision: 1.13 $ $Date: 2008-10-30 17:53:29 $
 */
public class SearchAjaxController implements ApplicationContextAware{


	/** Logger for this class and subclasses */
	protected final Log logger = LogFactory.getLog(getClass());
	
	
	/** the application context */
	private ApplicationContext applicationContext = null;
	
	/**
	 * Set the ApplicationContext that this object runs in. 
	 * @param applicationContext the current {@link ApplicationContext} 
	 */
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.applicationContext = applicationContext;		
	}

	/**
	 * Search for Resources that are related to the given keywords.
	 * @param keywords the keyword query string
	 * @param andConnected should be <code>true</code> if resources have to match all keywords.
	 * @return an array of IDs of the matching resources
	 */
	public String[] searchForGroupMeResources(String keywords, String andConnected){
		boolean keywordsAndConnected = Boolean.getBoolean(andConnected);
		
		SearchManager searchManager = (SearchManager) applicationContext.getBean(VIEW_VOCABULARY.GROUPME_SEARCH_MANGER, SearchManager.class);

		List<Resource> suitingResources = null;
		try {
			//searching only for resources (groups are excluded)
			suitingResources = searchManager.searchForResources(keywords, keywordsAndConnected, true);
			
			logger.info(suitingResources == null ? "No resources were found while searching for " + keywords : "Number of results (while searching for " + keywords + "): " + suitingResources.size());
		} catch (SearchEngineException e) {
			e.printStackTrace();
			logger.error("Error while searching for resources using the internal GrupMe! search engine.", e);
		}
		String[] resourceIDs = new String[suitingResources.size()];
		for(int i=0; i< suitingResources.size(); i++){
			resourceIDs[i] = suitingResources.get(i).getId();
		}
		return resourceIDs;
	}
	
	/**
	 * Search for Groups that are related to the given keywords.
	 * @param keywords the keyword query string
	 * @param andConnected should be <code>true</code> if groups have to match all keywords.
	 * @return an array of IDs of the matching groups
	 */
	public String[] searchForGroupMeGroups(String keywords, String andConnected){
		boolean keywordsAndConnected = Boolean.getBoolean(andConnected);
		
		SearchManager searchManager = (SearchManager) applicationContext.getBean(VIEW_VOCABULARY.GROUPME_SEARCH_MANGER, SearchManager.class);
		
		List<Group> suitingGroups = null;
		try {
			suitingGroups = searchManager.searchForGroups(keywords, keywordsAndConnected);
			
			logger.info(suitingGroups == null ? "No Groups were found while searching for " + keywords : "Number of results (while searching for " + keywords + "): " + suitingGroups.size());
		} catch (SearchEngineException e) {
			e.printStackTrace();
			logger.error("Error while searching for groups using the internal GrupMe! search engine.", e);
		}
		
		String[] groupIDs = new String[suitingGroups.size()];
		for(int i=0; i< suitingGroups.size(); i++){
			groupIDs[i] = suitingGroups.get(i).getId();
		}
		return groupIDs;
	}
	
	/**
	 * This method recomputes the tag cloud (either the overall tag cloud or the user-specific tag cloud).
	 * 
	 * @param startOfPeriod start of period (suggested format: yyyy-mm-dd). If this is <code>null</code> then all tag occurences that have 
	 * a date lesser than <i>endOfPeriod</i> are considered. 
	 * @param endOfPeriod end of period (suggested format: yyyy-mm-dd). If this is <code>null</code> then all tag occurences that have 
	 * a date greater than <i>beginOfPeriod</i> are considered. 
	 * @param user should be <code>true</code> if only the tags of the actual user should be included.  

	 * @return an HTML-formatted tag cloud
	 */
	public HtmlFormattedElement getTagCloud(String startOfPeriod, String endOfPeriod, Boolean userSpecific){
		logger.info("Visualizing tag cloud - start of period: " + startOfPeriod + " - end of period: " + endOfPeriod + " - for actual user only?: " +userSpecific);
		User user = null;
		if(userSpecific){
			UserManager userManager = (UserManager) applicationContext.getBean(VIEW_VOCABULARY.USER_MANGER, UserManager.class);
			user = userManager.getUser();
		}
		
		Calendar start = null;
		Calendar end = null;
		try{
			if(startOfPeriod != null && !"".equals(startOfPeriod)){
				DateFormat from = DateFormat.getDateInstance();
				from.parse(startOfPeriod);
				start = from.getCalendar();
			}
			if(endOfPeriod != null && !"".equals(endOfPeriod)){
				DateFormat	to = DateFormat.getDateInstance();
				to.parse(endOfPeriod);
				end = to.getCalendar();
			}
		}catch (Exception e){
			logger.error("Date format is wrong. Should be like:  yyyy-mm-dd", e);
		}
		SearchManager searchManager =(SearchManager) applicationContext.getBean(VIEW_VOCABULARY.GROUPME_SEARCH_MANGER, SearchManager.class);
		List<WeightedTag> weightedTags = searchManager.computeTagCloud(start, end, user, SEARCH_CONFIGURATION.TAG_CLOUD_BIG_MAX_NUMBER_OF_TAGS);
		
		HtmlFormattedElement tagCloud = new HtmlFormattedElement(null, new TagRenderer().renderTagCloud(weightedTags));
		searchManager.setCachedTagCloud(tagCloud);
		return tagCloud;
	}
	
	/**
	 * This method recomputes the tag cloud to be displayed in the sidebar on the GroupMe start page.
	 * 
	 * @return an HTML-formatted tag cloud
	 */
	public HtmlFormattedElement getSidebarTagCloud(){
		logger.info("Started computing sidebar-tagcloud.");
		Calendar endTime = Calendar.getInstance();
		Calendar startTime = Calendar.getInstance();
		startTime.setTimeInMillis(System.currentTimeMillis()-(SEARCH_CONFIGURATION.TAG_CLOUD_TIME_PERIOD_OF_RECENT_TAG_CLOUD * 24 * 60 * 60 * 1000));
		SearchManager searchManager =(SearchManager) applicationContext.getBean(VIEW_VOCABULARY.GROUPME_SEARCH_MANGER, SearchManager.class);
		List<WeightedTag> weightedTags = searchManager.computeTagCloud(startTime, endTime, null, SEARCH_CONFIGURATION.TAG_CLOUD_BIG_MAX_NUMBER_OF_TAGS);
		
		//Notfall (zu wenig tags):
		if(weightedTags == null || weightedTags.size() < SEARCH_CONFIGURATION.TAG_CLOUD_MIDDLE_MAX_NUMBER_OF_TAGS){
			logger.info("Too few recent tags (" + weightedTags.size() + ") -> we just take the complete tag cloud.");
			weightedTags = searchManager.computeTagCloud(null, null, null, SEARCH_CONFIGURATION.TAG_CLOUD_BIG_MAX_NUMBER_OF_TAGS);
		}
		HtmlFormattedElement tagCloud = new HtmlFormattedElement(null, new TagRenderer().renderTagCloud(weightedTags));
		
		return tagCloud;
	}
	
	/**
	 * This method recomputes the complete tag cloud containing all GroupMe tags.
	 * 
	 * @return an HTML-formatted tag cloud
	 */
	public HtmlFormattedElement getCompleteTagCloud(){
		logger.info("Started computing tag cloud with all groupme tags.");
		SearchManager searchManager =(SearchManager) applicationContext.getBean(VIEW_VOCABULARY.GROUPME_SEARCH_MANGER, SearchManager.class);
		List<WeightedTag> weightedTags = searchManager.computeTagCloud(null, null, null, null);
		HtmlFormattedElement tagCloud = new HtmlFormattedElement(null, new TagRenderer().renderTagCloud(weightedTags));
		
		return tagCloud;
	}
	
	/**
	 * This method returns the tag cloud of a group which is an ordered list of {@link WeightedTag} instances. 
	 * Ordering is done according to the number of occurences in the given period within the group. 
	 *  
	 * @param groupURI the URI of the group for which the tag cloud should be computed 
	 * @return an HTML-formatted tag cloud of the given group
	 */
	public HtmlFormattedElement getGroupTagCloudByURI(String groupURI){
		logger.info("Starting to compute tag cloud for: " + groupURI);
		Group group = null;
		try {
			group = ObjectFactory.getGroup(new URI(groupURI));
		} catch (URISyntaxException e) {
			logger.error("Given URI (" + groupURI + ") is not valid.", e);
		}
		SearchManager searchManager =(SearchManager) applicationContext.getBean(VIEW_VOCABULARY.GROUPME_SEARCH_MANGER, SearchManager.class);
		List<WeightedTag> weightedTags = searchManager.computeTagCloud(group, SEARCH_CONFIGURATION.TAG_CLOUD_MIDDLE_MAX_NUMBER_OF_TAGS);
		HtmlFormattedElement tagCloud = new HtmlFormattedElement(null, new TagRenderer().renderTagCloud(weightedTags));
		return tagCloud;
	}
	
	/**
	 * This method returns the tag cloud of a group which is an ordered list of {@link WeightedTag} instances. 
	 * Ordering is done according to the number of occurences in the given period within the group. 
	 *  
	 * @param groupID the ID of the group for which the tag cloud should be computed 
	 * @return an HTML-formatted tag cloud of the given group
	 */
	public HtmlFormattedElement getGroupTagCloudByID(String groupID){
		logger.info("Starting to compute tag cloud for: " + groupID);
		Group group = ObjectFactory.getGroup(groupID);
		
		SearchManager searchManager =(SearchManager) applicationContext.getBean(VIEW_VOCABULARY.GROUPME_SEARCH_MANGER, SearchManager.class);
		List<WeightedTag> weightedTags = searchManager.computeTagCloud(group, SEARCH_CONFIGURATION.TAG_CLOUD_MIDDLE_MAX_NUMBER_OF_TAGS);
		HtmlFormattedElement tagCloud = new HtmlFormattedElement(null, new TagRenderer().renderTagCloud(weightedTags));
		return tagCloud;
	}
}
